package com.zhanglinwei.openApiClient.segment;

import com.zhanglinwei.openApiClient.enums.SQLFunction;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.zhanglinwei.common.constants.StringPoolConstant.*;

@Getter
@Setter
public class SelectSegment implements SQLSegment{

    private boolean selectDistinct = false;
    private final List<String> selectSegmentList = new ArrayList<>();
    private final List<FunctionSelectItem> functionSelectSegmentList = new ArrayList<>();

    @Override
    public boolean isEmpty() {
        return CollectionUtils.isEmpty(selectSegmentList) && CollectionUtils.isEmpty(functionSelectSegmentList);
    }

    @Override
    public void add(SQLSegment... sqlSegments) {
        if (ArrayUtils.isNotEmpty(sqlSegments)) {
            Arrays.asList(sqlSegments).forEach(item -> selectSegmentList.add(item.sqlSegment()));
        }
    }

    public Set<String> columns() {
        if (isEmpty()) {
            return Collections.emptySet();
        }

        HashSet<String> columnSet = new HashSet<>();
        if (!selectSegmentList.isEmpty()) {
            columnSet.addAll(selectSegmentList);
        }
        if (!functionSelectSegmentList.isEmpty()) {
            columnSet.addAll(functionSelectSegmentList.stream().map(FunctionSelectItem::getColumn).collect(Collectors.toSet()));
        }

        return columnSet;
    }

    public Set<String> alias() {
        return isEmpty() ? Collections.emptySet() : functionSelectSegmentList.stream().map(FunctionSelectItem::getAlias).collect(Collectors.toSet());
    }

    public void addFunctionSelect(SQLSegment... sqlSegments) {
        if (ArrayUtils.isNotEmpty(sqlSegments)) {
            Arrays.stream(sqlSegments)
                    .filter(item -> item instanceof FunctionSelectItem)
                    .forEach(item -> functionSelectSegmentList.add((FunctionSelectItem) item));
        }
    }

    @Override
    public String sqlSegment() {
        StringJoiner joiner = new StringJoiner(" ");
        joiner.add(SQLKeyword.SELECT.sqlSegment());
        if (selectDistinct) {
            joiner.add(SQLKeyword.DISTINCT.sqlSegment());
        }
        if (isEmpty()) {
            joiner.add(SQLKeyword.SELECT_ALL.sqlSegment());
        } else {
            StringJoiner columnsJoiner = new StringJoiner(COMMA_SPACE);

            if (!selectSegmentList.isEmpty()) {
                joiner.add(String.join(COMMA_SPACE, selectSegmentList));
            }
            if (!functionSelectSegmentList.isEmpty()) {
                joiner.add(functionSelectSegmentList.stream().map(FunctionSelectItem::sqlSegment).collect(Collectors.joining(COMMA_SPACE)));
            }

            joiner.add(columnsJoiner.toString());
        }

        return joiner.toString();
    }

    @Getter(AccessLevel.PRIVATE)
    public static class FunctionSelectItem implements SQLSegment {
        private static final long serialVersionUID = -4478248783901726590L;

        private final SQLFunction function;
        private final String column;
        private final String alias;

        public FunctionSelectItem(SQLFunction function, String column, String alias) {
            this.function = function;
            this.column = column;
            this.alias = alias;
        }

        @Override
        public String sqlSegment() {
            String segment = String.format(function.getExpression(), column);
            return StringUtils.isBlank(alias) ? segment : segment + SQLKeyword.AS.sqlSegment() + alias;
        }
    }

}
